package com.lipaluma.xml.mapping;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlElements;

import org.apache.commons.lang.BooleanUtils;

import com.lipaluma.field.mapper.FieldMapper;
import com.lipaluma.field.merge.FieldMergedFunction;
import com.lipaluma.xml.adaptor.XmlAdaptor;
import com.lipaluma.xml.adaptor.XmlCustomAdaptor;
import com.lipaluma.xml.adaptor.XmlStringReplaceAdaptor;
import com.lipaluma.xml.adaptor.XmlTrimAdaptor;
import com.lipaluma.xml.adaptor.XmlTruncateAdaptor;
import com.lipaluma.xml.merge.XmlCustomMerge;
import com.lipaluma.xml.merge.XmlFieldMerged;
import com.lipaluma.xml.merge.XmlStrategyMerge;
import com.lipaluma.xml.validator.XmlAfterValidator;
import com.lipaluma.xml.validator.XmlBeforeValidator;
import com.lipaluma.xml.validator.XmlConstantValidator;
import com.lipaluma.xml.validator.XmlContainsValidator;
import com.lipaluma.xml.validator.XmlCustomValidator;
import com.lipaluma.xml.validator.XmlEndsWithValidator;
import com.lipaluma.xml.validator.XmlExcludedOfValidator;
import com.lipaluma.xml.validator.XmlIncludedInValidator;
import com.lipaluma.xml.validator.XmlLengthValidator;
import com.lipaluma.xml.validator.XmlMatchesValidator;
import com.lipaluma.xml.validator.XmlMaxValidator;
import com.lipaluma.xml.validator.XmlMinValidator;
import com.lipaluma.xml.validator.XmlNotContainsValidator;
import com.lipaluma.xml.validator.XmlNotEmptyValidator;
import com.lipaluma.xml.validator.XmlNotEndsWithValidator;
import com.lipaluma.xml.validator.XmlNotStartsWithValidator;
import com.lipaluma.xml.validator.XmlRangeValidator;
import com.lipaluma.xml.validator.XmlStartsWithValidator;
import com.lipaluma.xml.validator.XmlUniqueValidator;
import com.lipaluma.xml.validator.XmlValidator;

@XmlAccessorType(XmlAccessType.FIELD)
public abstract class AbstractXmlFieldMapping implements XmlFieldMapping {
	public static final String ADAPTORS_ON_EACH_VALUE = "adaptors-on-each-value";
	public static final String VALIDATORS_ON_EACH_VALUE = "validators-on-each-value";
	public static final String EACH_VALUE_REQUIRED = "each-value-required";
	public static final String FALSE_VALUES = "false-values";
	public static final String TRUE_VALUES = "true-values";

	@XmlAttribute(required=true)
	private String field;
	@XmlAttribute
	private String pattern = "";
	@XmlAttribute
	private String locale = "";
	@XmlAttribute
	private Boolean required;
	@XmlAttribute(name=EACH_VALUE_REQUIRED)
	private Boolean eachValueRequired;
	@XmlAttribute
	private Boolean trim;
	@XmlAttribute
	private String separator = "";
	@XmlAttribute
	private Boolean keepAll = false;
	@XmlElementWrapper(name=FALSE_VALUES)
	@XmlElement(name="value")
	private String[] falseValues;
	@XmlElementWrapper(name=TRUE_VALUES)
	@XmlElement(name="value")
	private String[] trueValues;
	@XmlAttribute(name="one-to_many")
	private Boolean oneToMany;
	@XmlAttribute
	private Boolean id;
	
	@XmlElements({
		@XmlElement(name=XmlStrategyMerge.FIELD_MERGED, type=XmlStrategyMerge.class),
		@XmlElement(name=XmlCustomMerge.FIELD_MERGED_CUSTOM, type=XmlCustomMerge.class)
	})
	private XmlFieldMerged fieldMerged;
	
	@XmlElements({
		@XmlElement(name=XmlTrimAdaptor.TRIM, type=XmlTrimAdaptor.class),
		@XmlElement(name=XmlTruncateAdaptor.TRUNCATE, type=XmlTruncateAdaptor.class),
		@XmlElement(name=XmlStringReplaceAdaptor.REPLACE, type=XmlStringReplaceAdaptor.class),
		@XmlElement(name=XmlCustomAdaptor.ADAPT, type=XmlCustomAdaptor.class)
	})
	@XmlElementWrapper
	private List<? extends XmlAdaptor> adaptors;

	@XmlElements({
		@XmlElement(name=XmlUniqueValidator.UNIQUE, type=XmlUniqueValidator.class),
		@XmlElement(name=XmlConstantValidator.CONSTANT, type=XmlConstantValidator.class),
		@XmlElement(name=XmlMinValidator.MIN, type=XmlMinValidator.class),
		@XmlElement(name=XmlMaxValidator.MAX, type=XmlMaxValidator.class),
		@XmlElement(name=XmlRangeValidator.RANGE, type=XmlRangeValidator.class),
		@XmlElement(name=XmlIncludedInValidator.INCLUDED_IN, type=XmlIncludedInValidator.class),
		@XmlElement(name=XmlExcludedOfValidator.EXCLUDED_OF, type=XmlExcludedOfValidator.class),
		@XmlElement(name=XmlLengthValidator.LENGTH, type=XmlLengthValidator.class),
		@XmlElement(name=XmlContainsValidator.CONTAINS, type=XmlContainsValidator.class),
		@XmlElement(name=XmlNotContainsValidator.NOT_CONTAINS, type=XmlNotContainsValidator.class),
		@XmlElement(name=XmlStartsWithValidator.STARTS_WITH, type=XmlStartsWithValidator.class),
		@XmlElement(name=XmlNotStartsWithValidator.NOT_STARTS_WITH, type=XmlNotStartsWithValidator.class),
		@XmlElement(name=XmlEndsWithValidator.ENDS_WITH, type=XmlEndsWithValidator.class),
		@XmlElement(name=XmlNotEndsWithValidator.NOT_ENDS_WITH, type=XmlNotEndsWithValidator.class),
		@XmlElement(name=XmlMatchesValidator.MATCHES, type=XmlMatchesValidator.class),
		@XmlElement(name=XmlNotEmptyValidator.NOT_EMPTY, type=XmlNotEmptyValidator.class),
		@XmlElement(name=XmlAfterValidator.AFTER, type=XmlAfterValidator.class),
		@XmlElement(name=XmlBeforeValidator.BEFORE, type=XmlBeforeValidator.class),
		@XmlElement(name=XmlCustomValidator.VALIDATE, type=XmlCustomValidator.class)
	})
	@XmlElementWrapper
	private List<? extends XmlValidator> validators;
	
	@XmlElements({
		@XmlElement(name=XmlTrimAdaptor.TRIM, type=XmlTrimAdaptor.class),
		@XmlElement(name=XmlTruncateAdaptor.TRUNCATE, type=XmlTruncateAdaptor.class),
		@XmlElement(name=XmlStringReplaceAdaptor.REPLACE, type=XmlStringReplaceAdaptor.class),
		@XmlElement(name=XmlCustomAdaptor.ADAPT, type=XmlCustomAdaptor.class)
	})
	@XmlElementWrapper(name=ADAPTORS_ON_EACH_VALUE)
	private List<? extends XmlAdaptor> adaptorsOnEachValue;

	@XmlElements({
		@XmlElement(name=XmlUniqueValidator.UNIQUE, type=XmlUniqueValidator.class),
		@XmlElement(name=XmlConstantValidator.CONSTANT, type=XmlConstantValidator.class),
		@XmlElement(name=XmlMinValidator.MIN, type=XmlMinValidator.class),
		@XmlElement(name=XmlMaxValidator.MAX, type=XmlMaxValidator.class),
		@XmlElement(name=XmlRangeValidator.RANGE, type=XmlRangeValidator.class),
		@XmlElement(name=XmlIncludedInValidator.INCLUDED_IN, type=XmlIncludedInValidator.class),
		@XmlElement(name=XmlExcludedOfValidator.EXCLUDED_OF, type=XmlExcludedOfValidator.class),
		@XmlElement(name=XmlLengthValidator.LENGTH, type=XmlLengthValidator.class),
		@XmlElement(name=XmlContainsValidator.CONTAINS, type=XmlContainsValidator.class),
		@XmlElement(name=XmlNotContainsValidator.NOT_CONTAINS, type=XmlNotContainsValidator.class),
		@XmlElement(name=XmlStartsWithValidator.STARTS_WITH, type=XmlStartsWithValidator.class),
		@XmlElement(name=XmlNotStartsWithValidator.NOT_STARTS_WITH, type=XmlNotStartsWithValidator.class),
		@XmlElement(name=XmlEndsWithValidator.ENDS_WITH, type=XmlEndsWithValidator.class),
		@XmlElement(name=XmlNotEndsWithValidator.NOT_ENDS_WITH, type=XmlNotEndsWithValidator.class),
		@XmlElement(name=XmlMatchesValidator.MATCHES, type=XmlMatchesValidator.class),
		@XmlElement(name=XmlNotEmptyValidator.NOT_EMPTY, type=XmlNotEmptyValidator.class),
		@XmlElement(name=XmlAfterValidator.AFTER, type=XmlAfterValidator.class),
		@XmlElement(name=XmlBeforeValidator.BEFORE, type=XmlBeforeValidator.class),
		@XmlElement(name=XmlCustomValidator.VALIDATE, type=XmlCustomValidator.class)
	})
	@XmlElementWrapper(name=VALIDATORS_ON_EACH_VALUE)
	private List<? extends XmlValidator> validatorsOnEachValue;

	public String getField() {
		return field;
	}
	
	public String getPattern() {
		return pattern;
	}

	public String getLocale() {
		return locale;
	}

	public boolean isRequired() {
		return BooleanUtils.toBoolean(required);
	}
	
	public boolean isEachValueRequired() {
		return BooleanUtils.toBoolean(eachValueRequired);
	}
	
	public boolean isTrim() {
		return BooleanUtils.toBoolean(trim);
	}

	public String getSeparator() {
		return separator;
	}

	public boolean isKeepAll() {
		return BooleanUtils.toBoolean(keepAll);
	}

	public String[] getFalseValues() {
		if(falseValues == null)
			return new String[0];
		return falseValues;
	}

	public String[] getTrueValues() {
		if(trueValues == null)
			return new String[0];
		return trueValues;
	}

	public List<? extends XmlAdaptor> getAdaptors() {
		if(adaptors == null)
			return new ArrayList<XmlAdaptor>();
		return adaptors;
	}

	public List<? extends XmlValidator> getValidators() {
		if(validators == null)
			return new ArrayList<XmlValidator>();
		return validators;
	}
	
	public List<? extends XmlAdaptor> getAdaptorsOnEachValue() {
		if(adaptorsOnEachValue == null)
			return new ArrayList<XmlAdaptor>();
		return adaptorsOnEachValue;
	}

	public List<? extends XmlValidator> getValidatorsOnEachValue() {
		if(validatorsOnEachValue == null)
			return new ArrayList<XmlValidator>();
		return validatorsOnEachValue;
	}
	
	public boolean isOneToMany() {
		return BooleanUtils.toBoolean(oneToMany);
	}

	@Override
	public boolean isId() {
		return BooleanUtils.toBoolean(id);
	}

	@Override
	public boolean fieldMerged() {
		return fieldMerged != null;
	}
	
	@Override
	public FieldMergedFunction mergedBy() {
		if(fieldMerged == null)
			return null;
		return new FieldMergedFunction(fieldMerged.getAlgorithm(), fieldMerged.getParams());
	}
	
	public abstract Class<? extends FieldMapper> with();	
	public abstract boolean isMultiple();	
}


